package com.cc.demo.mesage.service;

import com.solacesystems.jcsmp.BytesMessage;
import com.solacesystems.jcsmp.Destination;
import org.springframework.integration.acks.AcknowledgmentCallback;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.support.ErrorMessage;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;

@Service
public class SolaceMessageService implements MessageService {

    private static final String ACKNOWLEDGMENT_CALLBACK = "acknowledgmentCallback";

    @Override
    public Optional<String> getDestinationName(Message<?> message) {
        Optional<String> destinationName = Optional.empty();
        MessageHeaders headers = message.getHeaders();
        if (!headers.isEmpty()) {
            AcknowledgmentCallback acknowledgmentCallback = (AcknowledgmentCallback) message.getHeaders().get(ACKNOWLEDGMENT_CALLBACK);
            if (acknowledgmentCallback != null) {
                try {
                    Field xmlMessage = acknowledgmentCallback.getClass().getDeclaredField("xmlMessage");
                    xmlMessage.setAccessible(true);
                    BytesMessage bytesMessage = (BytesMessage) xmlMessage.get(acknowledgmentCallback);
                    if (bytesMessage != null) {
                        Destination destination = bytesMessage.getDestination();
                        destinationName = Optional.ofNullable(destination.getName());
                    }
                } catch (NoSuchFieldException | IllegalAccessException e) {
                    return Optional.empty();
                }
            }
        }
        return destinationName;
    }

    @Override
    public Optional<String> getDestinationName(ErrorMessage errorMessage) {
        return Optional.empty();
    }

    @Override
    public Optional<Date> getReceivedTime(Message<?> message) {
        MessageHeaders headers = message.getHeaders();
        if (!headers.isEmpty()) {
            Long time = (Long) message.getHeaders().get("timestamp");
            if (time != null) {
                return Optional.of(new Date(time));
            }
        }
        return Optional.empty();
    }

    @Override
    public Optional<Date> getReceivedTime(ErrorMessage errorMessage) {
        return Optional.empty();
    }

    @Override
    public Optional<String> getMessageContent(ErrorMessage errorMessage) {
        return Optional.empty();
    }

    @Override
    public Optional<Integer> getDeliveryTimes(ErrorMessage errorMessage) {
        return Optional.empty();
    }

    @Override
    public Optional<Integer> getDeliveryTimes(Message<?> message) {
        MessageHeaders headers = message.getHeaders();
        if (!headers.isEmpty()) {
            AtomicInteger times = ((AtomicInteger) message.getHeaders().get("deliveryAttempt"));
            if (times != null) {
                return Optional.of(times.intValue());
            }
        }
        return Optional.empty();
    }

    @Override
    public Optional<String> getContentType(Message<?> message) {
        Optional<String> contentType = Optional.empty();
        MessageHeaders headers = message.getHeaders();
        if (!headers.isEmpty()) {
            Object type = message.getHeaders().get("contentType");
            if (type != null) {
                contentType = Optional.ofNullable(type.toString());
            }
        }
        return contentType;
    }

    @Override
    public Optional<Long> getDuration(Message<?> message) {
        //TODO 时区问题
        Optional<Date> receivedTime = this.getReceivedTime(message);
        if (receivedTime.isEmpty()) {
            return Optional.empty();
        }
        Instant receivedInstant = Instant.ofEpochMilli(receivedTime.get().getTime());
        Duration duration = Duration.between(receivedInstant, Instant.now());
        return Optional.of(duration.toMillis());
    }
}
